Pingコマンドのパケットサイズについて調べてみた
しばたです。
先日リリースされたAmazon CloudWatch Network Monitorについて動作検証をしています。
その中でICMPエコー要求による監視パラメーターの一つに「パケットサイズ」があります。
ここで「そういえばICMPエコー要求のパケットサイズについて意識したことが無いな。」と思い、代表的なPingコマンドについてパケットサイズ関連の情報を調べてみました。
なお、今回はIPv4のみ対象としています。
ICMPメッセージフォーマットとPingコマンドの基本
IPv4におけるICMPエコー要求およびエコー応答におけるメッセージフォーマットはRFC792(とそのアップデート)で定義されており以下のフォーマットになっています。
Echo or Echo Reply Message
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ...
+-+-+-+-+-
最初の4ByteはICMP共通のヘッダで、TypeについてはType 8 = エコー要求
、Type 0 = エコー応答
、Codeは常に0
となります。
次の4ByteはIDとシーケンス番号で設定する内容は実装依存となります。
最後に可変長のデータ領域(ペイロード)となり、パケットサイズを変える際はここのサイズが変わることになります。
そしてPingコマンドはOS毎で実装が異なり送出されるパケットの内容はそれぞれ独自の内容となります。
LinuxにおけるPingコマンド
今回はAmazon Linux 2023環境を調査対象にしました。
LinuxにおけるPing実装は大きくiputilsとInetutilsに分かれる様ですが、Amazon Linuxはiputils実装が採用されています。
LinuxにおけるPingコマンドでは-s
パラメーターでパケットサイズを調整可能です。
デフォルト値は56
Byteで先頭8Byteのヘッダ(Typeからシーケンス番号まで)と合わせてICMP全体で64Byteになる様にされています。
実行例はこんな感じです。
# -s パラメーターでパケットサイズを調整可能。デフォルト 56
$ ping -c 4 -s 56 10.0.11.15
PING 10.0.11.15 (10.0.11.15) 56(84) bytes of data.
64 bytes from 10.0.11.15: icmp_seq=1 ttl=127 time=0.751 ms
64 bytes from 10.0.11.15: icmp_seq=2 ttl=127 time=0.479 ms
64 bytes from 10.0.11.15: icmp_seq=3 ttl=127 time=0.621 ms
64 bytes from 10.0.11.15: icmp_seq=4 ttl=127 time=0.529 ms
--- 10.0.11.15 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3083ms
rtt min/avg/max/mdev = 0.479/0.595/0.751/0.103 ms
出力メッセージのうち
PING 10.0.11.15 (10.0.11.15) 56(84) bytes of data.
の部分の56
が指定サイズ、(84)
がIPヘッダを含めたサイズ(56 (ペイロード) + 8 (ICMPヘッダ) + 20 (IPヘッダ)
)となります。
そしてパケットキャプチャした結果はこんな感じでした。
IDとシーケンス番号にはそれぞれ固有の値がセットされていました。
データ領域の内、最初の16Byteにはタイムスタンプがセットされ残りは0x10
から始まるバイト列が埋められる形になっています。
このバイト列は0x10
を起点に0x10, 0x11, 0x12, ~, 0xff, 0x00, 0x01, ~
の順でループする構造となっていました。
そして-s
の値が16より少ない場合はタイムスタンプは記録されず0x00 ~ 0x0e
までのバイト列のみ埋められます。
ここまでをまとめると以下の様になります。
-s
< 16の場合0x00
から始まるバイト列 :-s
の値 Byte
-s
>= 16の場合- タイムスタンプ : 16Byte
0x11
から始まるバイト列 : (-s
の値 - 16) Byte
macOSにおけるPingコマンド
Mac環境は私物のMac Mini(macOS Sonoma 14.2.1)を調査対象にしました。
macOSにおけるPing実装はBSD派生の独自の様[1]ですが、パケットサイズについてはLinux同様-s
パラメーターで設定可能であり、デフォルト値も同じ56
となります。
実行例はこんな感じ。
# -s パラメーターでパケットサイズを調整可能。デフォルト 56
% ping -c 4 -s 56 192.168.11.70
PING 192.168.11.70 (192.168.11.70): 56 data bytes
64 bytes from 192.168.11.70: icmp_seq=0 ttl=128 time=4.028 ms
64 bytes from 192.168.11.70: icmp_seq=1 ttl=128 time=5.500 ms
64 bytes from 192.168.11.70: icmp_seq=2 ttl=128 time=7.003 ms
64 bytes from 192.168.11.70: icmp_seq=3 ttl=128 time=4.568 ms
--- 192.168.11.70 ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 4.028/5.275/7.003/1.128 ms
こちらの出力メッセージは
PING 192.168.11.70 (192.168.11.70): 56 data bytes
と、指定サイズの56
だけ表示されていますが、ICMP全体のサイズは追加で8Byte、IP全体ではさらにIPヘッダのサイズを足す必要がありますのでご注意ください。
パケットキャプチャした結果はこんな感じです。
こちらもIDとシーケンス番号両方に固有の値が設定されますが、IDについては最初のパケットは常に0になっていました。
データ領域の内、最初の8Byteにはタイムスタンプがセットされ残りは0x08
から始まり0xff
までを繰り返すバイト列が埋められる形になっています。
-s
の値が8より少ない場合はタイムスタンプは記録されず全てのバイトが0x00
で埋められました。
まとめると以下の通りです。
-s
< 8の場合- 全て
0x00
のバイト列 :-s
の値 Byte
- 全て
-s
>= 8の場合- タイムスタンプ : 8Byte
0x08
から始まるバイト列 : (-s
の値 - 8) Byte
WindowsにおけるPingコマンド
Windows環境は日本語版のWindows Server 2022を調査対象にしています。
WindowsにおけるPing実装は非公開でありコマンド体系もLinuxやmacOSとかなり異なります。
パケットサイズについては-l
パラメーターで設定可能であり、デフォルト値は32
となります。
実行例はこんな感じ。
# -l パラメーターでパケットサイズを調整可能。デフォルト 32
PS C:\> ping -l 32 10.0.11.15
10.0.11.15 に ping を送信しています 32 バイトのデータ:
10.0.11.15 からの応答: バイト数 =32 時間 <1ms TTL=127
10.0.11.15 からの応答: バイト数 =32 時間 <1ms TTL=127
10.0.11.15 からの応答: バイト数 =32 時間 <1ms TTL=127
10.0.11.15 からの応答: バイト数 =32 時間 <1ms TTL=127
10.0.11.15 の ping 統計:
パケット数: 送信 = 4、受信 = 4、損失 = 0 (0% の損失)、
ラウンド トリップの概算時間 (ミリ秒):
最小 = 0ms、最大 = 0ms、平均 = 0ms
こちらの出力メッセージは
10.0.11.15 に ping を送信しています 32 バイトのデータ:
とmacOSと同じパターンになっており、ICMP全体のサイズは追加で8Byte、IP全体ではさらにIPヘッダのサイズを足す必要があります。
パケットキャプチャした結果はこんな感じです。
IDとシーケンス番号にはそれぞれ固有の値がセットされていました。
データ領域にタイムスタンプは設定されず、0x61 (a)
から0x77 (w)
を延々と繰り返すバイト列が設定されていました。
まとめると以下の通りです。
- 全て条件において
0x61 (a) ~ 0x77 (w)
を繰り返すバイト列 :-l
の値 Byte
Windowsだけパケットサイズのデフォルト値が異なるのでLinuxやmacOSと合わせるには-l 56
にする必要があります。
# LinuxやmacOSとパケットサイズを合わせるには -l 56 にする
ping -l 56 10.0.11.15
余談 : PowerShellの場合
Windows PowerShellおよびPowerShell 7(PowerShell Core)ではTest-ConnectionコマンドレットでPingと同等の行為が可能です。
どちらも.NETのネットワーク機能を使ってICMPパケットを送出しているのですが、.NET Core以降はクロスプラットフォーム化により内部的な実装+挙動が若干異なっています。
Windows PowerShellの場合
Test-Connection
コマンドでは-BufferSize
パラメーターでパケットサイズを調整可能で、デフォルト値は32
です。
送出されるパケットの内容もWindowsのPingコマンドと同じになる様に調整されていました。
# Test-Connection 実行例
PS C:\> Test-Connection -ComputerName 10.0.11.15 -BufferSize 32
Source Destination IPV4Address IPV6Address Bytes Time(ms)
------ ----------- ----------- ----------- ----- --------
EC2AMAZ-RQ... 10.0.11.15 10.0.11.15 32 0
EC2AMAZ-RQ... 10.0.11.15 10.0.11.15 32 0
EC2AMAZ-RQ... 10.0.11.15 10.0.11.15 32 0
EC2AMAZ-RQ... 10.0.11.15 10.0.11.15 32 2
パケットキャプチャの結果は以下。
(WindowsのPing同様にデータ領域は0x61 (a)
から0x77 (w)
で埋められる)
PowerShell 7の場合
PowerShell 7のTest-Connection
でも同様のパケットを送出する仕組みになっているのですが、.NET 7からの破壊的変更に対応する改修でバグを仕込んでしまっており怪しい挙動をします。
- Linux でのカスタム ping ペイロード
- Test-Connection needs to be revised due to an intentional breaking change in .NET 7
本日時点で最新のPowerShell 7.4.0においてもバグにより-BufferSize
パラメーターが未指定または-BufferSize 32
の場合はデータ領域に一切のデータが設定されません。
このためICMPパケットサイズは常に8Byteのままです。
(ただ、これでも疎通確認自体は問題無く可能です)
# -BufferSize 未指定および -BufferSize 32は無視されるバグがある
# ※32以外のサイズ指定なら期待した動作をする
PowerShell 7.4.0
PS C:\> Test-Connection -ComputerName 10.0.11.15 -BufferSize 32
Destination: 10.0.11.15
Ping Source Address Latency BufferSize Status
(ms) (B)
---- ------ ------- ------- ---------- ------
1 EC2AMAZ-RQEKLVQ 10.0.11.15 0 32 Success
2 EC2AMAZ-RQEKLVQ 10.0.11.15 0 32 Success
3 EC2AMAZ-RQEKLVQ 10.0.11.15 0 32 Success
4 EC2AMAZ-RQEKLVQ 10.0.11.15 0 32 Success
(バグによりデータ領域に何も設定されていない)
また、Linux環境においては非rootユーザーでは-BufferSize
パラメーターをデフォルト以外の値にすると権限エラーとなります。
# 非ルートユーザーの場合 -BufferSize を指定すると権限エラー
# ※ルートユーザーなら -BufferSizeを指定可能だが、-BufferSize 32は無視されるバグがあるので注意
PowerShell 7.4.0
PS /> Test-Connection 10.0.11.15 -BufferSize 56
Test-Connection: Unable to send custom ping payload. Run program under privileged user account or grant cap_net_raw capability using setcap(8).
これ自体は.NET側の仕様変更によるものであるため仕方ないのですが、エラーを出さない様にするために-BufferSize 32
の場合が無視されるバグが発生しているので要注意です。
こちらについては時間があればIssueを上げておこうと思います。
取り急ぎはPowerShell 7のTest-Connection
は使わない方が良いでしょう。
正直Pingコマンドで事足りるので私は一切使っていません...[2]
まとめ
最後に全体を通した内容を簡単にまとめておきます。
- PingコマンドはOSにより実装が異なり送出されるICMPパケットの内容も異なる
- OSにより実装は異なるがパケットサイズを調整するパラメーターは存在する
- パラメーターで調整可能なサイズはICMPデータ領域(ペイロード)のサイズ
- ICMPパケットの全体のサイズを出すにはヘッダの8Byteを追加する必要がある
おわりに
以上となります。
軽い気持ちで調べ始めたのですがなかなかのボリュームになりました。
通常の疎通確認においてはそこまでパケットサイズに気を配ることは無いと思います。
本記事の内容はちょっとしたトリビアくらいに捉えてもらうのが良いでしょう。
どうもこれっぽい https://opensource.apple.com/source/network_cmds/network_cmds-433/ping.tproj/ ↩︎
本記事を書いてはじめてこのバグに気が付いたくらいですし ↩︎